package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"math/rand"
	"os"
	"os/exec"
	"sync"
	"time"
)

const (
	WIDTH  int = 30
	HEIGHT int = 25
)

var (
	engine *snake
)

type snake struct {
	body      []coordinate //蛇身体
	score     int          //得分
	food      chan int
	l         sync.RWMutex
	m         [][]int
	status    int //0移动 1停止 2结束
	direction int //方向 1上 2右 3下 4左
}

type coordinate struct {
	H int
	W int
}

func init() {
	engine = &snake{
		body: make([]coordinate, 0),
		m:    make([][]int, HEIGHT),
		food: make(chan int, 1),
	}
	engine.body = append(engine.body, coordinate{
		H: HEIGHT / 2,
		W: WIDTH / 2,
	})
	engine.loadMap()
}

func (s *snake) loadMap() {
	for h := 0; h < HEIGHT; h++ {
		row := make([]int, WIDTH)
		for w := 0; w < WIDTH; w++ {
			if h == 0 || w == 0 || h == HEIGHT-1 || w == WIDTH-1 {
				row[w] = 1
			} else {
				row[w] = 0
			}
		}
		s.m[h] = row
	}
	s.m[HEIGHT/2][WIDTH/2] = 2
	s.randFood()
}

func (s *snake) flushMap() {
	for {
		s.l.Lock()
		//清楚地图中的蛇
		for hk, row := range s.m {
			for wk, w := range row {
				if w == 2 {
					s.m[hk][wk] = 0
				}
			}
		}
		//在地图中画出蛇
		for _, v := range s.body {
			s.m[v.H][v.W] = 2
		}
		switch s.status {
		case 0:
			cmd := exec.Command("cmd", "/c", "cls") //如果是win使用该调命令
			cmd.Stdout = os.Stdout
			cmd.Run()
			for _, h := range s.m {
				for _, w := range h {
					switch w {
					case 1: //墙体
						fmt.Printf("▇")
					case 0: //空地
						fmt.Printf("  ")
					case 2: //蛇
						fmt.Printf("◎")
					case 3: //食物
						fmt.Printf("❦")
					}
				}
				fmt.Println()
			}
			time.Sleep(time.Millisecond * 100)
			s.l.Unlock()
		case 1: //停止游戏
			s.l.Unlock()
			break
		case 2: //结束游戏
			s.l.Unlock()
			fmt.Println("1秒后结束游戏")
			time.Sleep(time.Second)
			os.Exit(1)
		}
	}
}

func (s *snake) randFood() {
	for {
		num := 0
		rand.Seed(time.Now().UnixNano())
		h := rand.Intn(HEIGHT)
		w := rand.Intn(WIDTH)
		if h == 0 || h == HEIGHT-1 || w == 0 || w == WIDTH-1 {
			continue
		}
		for _, v := range s.body {
			if v.H == h && v.W == w {
				num++
			}
		}
		if num > 0 {
			continue
		}
		s.m[h][w] = 3
		return
	}
}

//移动
func (s *snake) move() {
	t := time.NewTicker(time.Millisecond * 300)
	for {
		s.l.Lock()
		select {
		case <-t.C:
			if s.status == 1 {
				break
			}
			switch s.direction {
			case 1: //向上
				if s.m[s.body[0].H-1][s.body[0].W] == 1 || s.m[s.body[0].H-1][s.body[0].W] == 2 {
					s.status = 2 //碰到物体结束游戏
					s.l.Unlock()
					return
				}
				if s.m[s.body[0].H-1][s.body[0].W] == 3 { //吃到食物
					s.randFood()
					coor := coordinate{}
					if len(s.body) <= 2 {
						//两个长度以内 反方向+1长度
						coor.H = s.body[len(s.body)-1].H + 1
						coor.W = s.body[len(s.body)-1].W
					} else {
						if s.body[len(s.body)-1].W-s.body[len(s.body)-2].W > 0 {
							coor.H = s.body[len(s.body)-1].H
							coor.W = s.body[len(s.body)-1].W + 1
						} else {
							coor.H = s.body[len(s.body)-1].H
							coor.W = s.body[len(s.body)-1].W - 1
						}
					}
					s.body = append(s.body, coor)
				}
			case 2: //向右
				if s.m[s.body[0].H][s.body[0].W+1] == 1 || s.m[s.body[0].H][s.body[0].W+1] == 2 {
					s.status = 2 //碰到物体结束游戏
					s.l.Unlock()
					return
				}
				if s.m[s.body[0].H][s.body[0].W+1] == 3 { //吃到食物
					s.randFood()
					coor := coordinate{}
					if len(s.body) <= 2 {
						//两个长度以内 反方向+1长度
						coor.H = s.body[len(s.body)-1].H
						coor.W = s.body[len(s.body)-1].W - 1
					} else {
						if s.body[len(s.body)-1].H-s.body[len(s.body)-2].H > 0 {
							coor.H = s.body[len(s.body)-1].H + 1
							coor.W = s.body[len(s.body)-1].W
						} else {
							coor.H = s.body[len(s.body)-1].H - 1
							coor.W = s.body[len(s.body)-1].W
						}
					}
					s.body = append(s.body, coor)
				}
			case 3: //向下
				if s.m[s.body[0].H+1][s.body[0].W] == 1 || s.m[s.body[0].H+1][s.body[0].W] == 2 {
					s.status = 2 //碰到物体结束游戏
					s.l.Unlock()
					return
				}
				if s.m[s.body[0].H+1][s.body[0].W] == 3 { //吃到食物
					s.randFood()
					coor := coordinate{}
					if len(s.body) <= 2 {
						//两个长度以内 反方向+1长度
						coor.H = s.body[len(s.body)-1].H - 1
						coor.W = s.body[len(s.body)-1].W
					} else {
						if s.body[len(s.body)-1].W-s.body[len(s.body)-2].W > 0 {
							coor.H = s.body[len(s.body)-1].H
							coor.W = s.body[len(s.body)-1].W + 1
						} else {
							coor.H = s.body[len(s.body)-1].H
							coor.W = s.body[len(s.body)-1].W - 1
						}
					}
					s.body = append(s.body, coor)
				}
			case 4: //向左
				if s.m[s.body[0].H][s.body[0].W-1] == 1 || s.m[s.body[0].H][s.body[0].W-1] == 2 {
					s.status = 2 //碰到物体结束游戏
					s.l.Unlock()
					return
				}
				if s.m[s.body[0].H][s.body[0].W-1] == 3 { //吃到食物
					s.randFood()
					coor := coordinate{}
					if len(s.body) <= 2 {
						//两个长度以内 反方向+1长度
						coor.H = s.body[len(s.body)-1].H
						coor.W = s.body[len(s.body)-1].W + 1
					} else {
						if s.body[len(s.body)-1].H-s.body[len(s.body)-2].H > 0 {
							coor.H = s.body[len(s.body)-1].H + 1
							coor.W = s.body[len(s.body)-1].W
						} else {
							coor.H = s.body[len(s.body)-1].H - 1
							coor.W = s.body[len(s.body)-1].W
						}
					}
					s.body = append(s.body, coor)
				}
			}
			for i := len(s.body) - 1; i >= 0; i-- {
				if i == 0 {
					switch s.direction {
					case 1: //向上
						s.body[i] = coordinate{
							H: s.body[i].H - 1,
							W: s.body[i].W,
						}
					case 2: //向右
						s.body[i] = coordinate{
							H: s.body[i].H,
							W: s.body[i].W + 1,
						}
					case 3: //向下
						s.body[i] = coordinate{
							H: s.body[i].H + 1,
							W: s.body[i].W,
						}
					case 4: //向左
						s.body[i] = coordinate{
							H: s.body[i].H,
							W: s.body[i].W - 1,
						}
					}
				} else {
					s.body[i] = s.body[i-1]
				}
			}
		}
		s.l.Unlock()
	}
}

//键盘事件监听
func (s *snake) control() {
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp: //小键盘向上
				s.l.Lock()
				s.direction = 1
				s.l.Unlock()
			case termbox.KeyArrowDown: //小键盘向下
				s.l.Lock()
				s.direction = 3
				s.l.Unlock()
			case termbox.KeyArrowLeft: //小键盘向左
				s.l.Lock()
				s.direction = 4
				s.l.Unlock()
			case termbox.KeyArrowRight: //小键盘向右
				s.l.Lock()
				s.direction = 2
				s.l.Unlock()
			case termbox.KeySpace:
				s.l.Lock()
				switch s.status {
				case 0:
					fmt.Println("暂停游戏")
					s.status = 1
				case 1:
					s.status = 0
				}
				s.l.Unlock()
			case termbox.KeyEnd: //end结束游戏
				s.l.Lock()
				s.status = 2 //结束
				s.l.Unlock()
			}
		}
	}
}

func main() {
	termbox.Init()
	go engine.flushMap()
	go engine.control()
	go engine.move()
	for {
	}
	termbox.Close()
}
